﻿using System;
using System.IO;
using System.Diagnostics;
using System.Collections.ObjectModel;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Runtime.InteropServices;

namespace Scheduler
{
    class ZProcess
    {
        protected string processDIR = null;
        protected string processFileName = null;
        protected string processArguments = null;
        protected int callIntervalMins = 0;

        private DateTime startTime = System.DateTime.MinValue;
        private DateTime nextTime = System.DateTime.MinValue;

        private Process oProcess = null;
        private ProcessStartInfo oInfo = null;

        public ZProcess() { }
        public ZProcess(string dir, string fn, string args, int mins)
        {
            this.processDIR = dir;
            this.processFileName = fn;
            this.processArguments = args;
            this.callIntervalMins = mins;
        }

        protected void log(string msg)
        {
            Console.WriteLine("[{0}]: <{1}>, {2}", System.DateTime.Now.ToLocalTime(), this.processFileName, msg);
        }

        public void start()
        {
            if (String.IsNullOrEmpty(this.processFileName) || String.IsNullOrEmpty(this.processDIR)) return;
            String CurDIR = Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName);

            oProcess = null;
            try
            {
                Directory.SetCurrentDirectory(this.processDIR);

                oInfo = new ProcessStartInfo(this.processFileName, this.processArguments);
                oInfo.CreateNoWindow = false;
                oInfo.UseShellExecute = true;
                oInfo.RedirectStandardError = false;
                oInfo.RedirectStandardOutput = false;
                oInfo.RedirectStandardInput = false;

                this.startTime = System.DateTime.Now.ToLocalTime();
                this.log("创建进程......");
                if (this.nextTime == System.DateTime.MinValue)
                {
                    //如果Next Time没有被设置，在Start Time上进行累加
                    this.nextTime = this.startTime.AddMinutes(this.callIntervalMins);
                }
                else
                {
                    //如果Next Time已经被设置，在Next Time上进行累加
                    this.nextTime = this.nextTime.AddMinutes(this.callIntervalMins);
                }
                Console.WriteLine("下次运行时间：{0}", this.nextTime);
                //Console.WriteLine("{0}", this.nextTime);

                oProcess = Process.Start(oInfo);
            }
            catch (System.ComponentModel.Win32Exception)
            {
                Console.WriteLine("系统找不到指定的程序文件 - {0}", this.processFileName);
                oProcess = null;
            }
            catch (Exception)
            {
                oProcess = null;
            }
            finally
            {
                Directory.SetCurrentDirectory(CurDIR);
            }
        }

        public void check()
        { 
            DateTime currTime = System.DateTime.Now.ToLocalTime();
            if (this.callIntervalMins <= 0) return; //Only once
            if (currTime >= this.nextTime)
            { 
                //Need be call again
                this.log("重调度进程......");
                this.stop();
                this.start();
            }
        }

        public void stop()
        {
            if (oProcess == null) return;

            try
            {
                //Wait 3 secs before kill this process
                //oProcess.WaitForExit(3 * 1000);
                if (!oProcess.HasExited)
                {
                    this.log("杀死进程！");
                    oProcess.Kill();
                }
                else
                {
                    this.log("进程已退出！");
                }
            }
            catch (Exception)
            {
            }
            finally
            {
                oProcess.Close();
                oProcess = null;
            }
        }
    }

    class ZScheduler
    {
        private static Collection<ZScheduler> schdList = new Collection<ZScheduler>();

        public static void addTask(string fn) 
        {
            ZScheduler schd = new ZScheduler(fn);
            schd.start();
            schdList.Add(schd);
            Thread.Sleep(5*1000); //Sleep 5 Secs to avoid confict
        }

        public static void refreshAllTasks()
        {
            foreach (ZScheduler schd in schdList)
            {
                schd.check();
            }
        }

        public static void endAllTasks()
        {
            foreach (ZScheduler schd in schdList)
            {
                schd.stop();
            }
        }

        public void start()
        { 
            //检查参数
            //string curDIR = Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName);
            if (String.IsNullOrEmpty(this.ProgramDIR) || String.IsNullOrEmpty(this.ConfigDIR) ||
                !Directory.Exists(this.ProgramDIR) || !Directory.Exists(this.ConfigDIR) ||
                String.IsNullOrEmpty(this.ExecuteDIR) )
            {
                Console.WriteLine("配置文件未指定目录，或者源目录不存在！");
                return;
            }
            if (String.IsNullOrEmpty(this.Main) || !File.Exists(this.ProgramDIR + this.Main))
            {
                Console.WriteLine("主程序不存在！");
                return;
            }

            //建立运行目录
            if (!Directory.Exists(this.ExecuteDIR))
            {
                Directory.CreateDirectory(this.ExecuteDIR);
            }

            //把需要的文件拷贝到运行目录
            Console.WriteLine("复制文件: {0} --> {1}", this.ConfigDIR, this.ExecuteDIR);
            foreach (string conf in this.ConfigList)
            {
                Console.WriteLine("复制: {0}", conf);
                if (!File.Exists(this.ConfigDIR + conf) || String.IsNullOrEmpty(conf))
                {
                    Console.WriteLine("Not exists!");
                    continue;
                }
                File.Copy(this.ConfigDIR + conf, this.ExecuteDIR + conf, true);
            }

            Console.WriteLine("复制文件: {0} --> {1}", this.ProgramDIR, this.ExecuteDIR);
            foreach (string prog in this.ProgramList)
            {
                Console.WriteLine("复制: {0}", prog);
                if (!File.Exists(this.ProgramDIR + prog) || String.IsNullOrEmpty(prog))
                {
                    Console.WriteLine("Not exists!");
                    continue;
                }
                File.Copy(this.ProgramDIR + prog, this.ExecuteDIR + prog, true);
            }

            Console.WriteLine("复制: {0}", this.Main);
            if (!File.Exists(this.ProgramDIR + this.Main))
            {
                Console.WriteLine("Not exists!");
                return;
            }
            File.Copy(this.ProgramDIR + this.Main, this.ExecuteDIR + this.Main, true);


            //通过Process执行相关操作
            this.zproc = new ZProcess( this.ExecuteDIR, this.Main, "", this.RepeatInterval );
            this.zproc.start();
        }

        public void check() 
        {
            if (this.zproc != null)
            {
                this.zproc.check();
            }
        }

        public void stop()
        {
            if (this.zproc != null)
            {
                this.zproc.stop();
            }
        }

        private string filename = null;
        private string ProgramDIR = null;
        private string ConfigDIR = null;
        private string ExecuteDIR = null;
        private string Main = null;
        private int RepeatInterval = 0;
        private Collection<string> ProgramList = new Collection<string>();
        private Collection<string> ConfigList = new Collection<string>();

        private ZProcess zproc = null;

        public ZScheduler(string fn)
        {
            this.filename = fn;

            this.loadConfig();
        }

        public void loadConfig()
        {
            try
            {
                string[] lines = File.ReadAllLines(this.filename);
                foreach (string line in lines)
                {
                    if (line.StartsWith("ProgramDIR=", StringComparison.OrdinalIgnoreCase))
                    {
                        this.ProgramDIR = GetStringValue(line);
                        continue;
                    }

                    if (line.StartsWith("ConfigDIR=", StringComparison.OrdinalIgnoreCase))
                    {
                        this.ConfigDIR = GetStringValue(line);
                        continue;
                    }

                    if (line.StartsWith("ExecuteDIR=", StringComparison.OrdinalIgnoreCase))
                    {
                        this.ExecuteDIR = GetStringValue(line);
                        continue;
                    }

                    if (line.StartsWith("Main=", StringComparison.OrdinalIgnoreCase))
                    {
                        this.Main = GetStringValue(line);
                        continue;
                    }

                    if (line.StartsWith("RepeatInterval=", StringComparison.OrdinalIgnoreCase))
                    {
                        this.RepeatInterval = GetIntegerValue(line);
                        continue;
                    }

                    if (line.StartsWith("Program=", StringComparison.OrdinalIgnoreCase))
                    {
                        this.ProgramList.Add(GetStringValue(line));
                        continue;
                    }

                    if (line.StartsWith("Config=", StringComparison.OrdinalIgnoreCase))
                    {
                        this.ConfigList.Add(GetStringValue(line));
                        continue;
                    }
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }

        public static int GetIntegerValue(string line)
        {
            string value = GetStringValue(line);

            if (string.IsNullOrEmpty(value))
            {
                return -1;
            }
            int id;

            if (int.TryParse(value, out id))
            {
                return id;
            }
            return -1;
        }

        public static string GetStringValue(string line)
        {
            return line.Substring(line.IndexOf('=') + 1);
        }

    }

    class Program
    {
        public static bool stopMe = false;

        static void Main(string[] args)
        {
            // Clear the screen.
            Console.Clear();

            Console.WriteLine("Press CTRL+C to Quit !");

            // Turn off the default system behavior when CTRL+C is pressed. When 
            // Console.TreatControlCAsInput is false, CTRL+C is treated as an
            // interrupt instead of as input.
            Console.TreatControlCAsInput = false;
            
            // Establish an event handler to process key press events.
            Console.CancelKeyPress += new ConsoleCancelEventHandler(myHandler);


            //程序正文，正常开始
            string appName = Process.GetCurrentProcess().MainModule.FileName;
            string[] filelist = Directory.GetFiles(Path.GetDirectoryName(appName), "*.ini");
            Array.Sort(filelist);

            foreach (string filename in filelist)
            {
                ZScheduler.addTask(filename);
            }

            while (true)
            {
                ZScheduler.refreshAllTasks();
                Thread.Sleep(1000);
                if (stopMe) break;
            }

            ZScheduler.endAllTasks();
        }

        protected static void myHandler(object sender, ConsoleCancelEventArgs args)
        {
            // Set the Cancel property to true to prevent the process from terminating.
            args.Cancel = true;
            stopMe = true;
        }
    }
}
